/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "coid/comm/binstream/netstreamcoid.h"
#include "coid/comm/pthreadx.h"
#include "acceptor.h"
//#include "tower.h"
#include "server.h"
#include "coid/coid.h"

#ifdef SYSTYPE_MSVC
#include <process.h>
#else
#include <unistd.h>
#endif

namespace coid {


uint AcceptorCoid::_pid = 0;


////////////////////////////////////////////////////////////////////////////////
bool AcceptorCoid::spawn()
{
    ++_nwaiting;
    if( 0 == spawn_thread( thread_fnc, this, false, _thrname ) )
        return true;
    --_nwaiting;
    return false;
}

////////////////////////////////////////////////////////////////////////////////
void* AcceptorCoid::thread_fnc (void* p)
{
    opcd e = ((AcceptorCoid*)p)->accept_connections();
    return (void*) e._ptr;
}

////////////////////////////////////////////////////////////////////////////////
opcd AcceptorCoid::find_service( const token& name, const version& svcver, NODEREF<ServiceCoid>& psvc )
{
    psvc.set_atexit_nothing();

    opcd e = SRVROOTA._objmgr.service_find( name, svcver, psvc );
    if(e)
    {
        dynarray<uint> lst;
        SRVROOTA._objmgr.service_find_all( name, lst );

        charstr ver;
        if( lst.size() == 0 )
        {
            post_trace(0) << "service not found: \"" << name << "\"" << EOM;
            return ersFE_NO_SUCH_SERVICE;
        }
        else
        {
            binstream& bs = post_trace(0);
            bs << "no matching version of service \"" << name << "\" can be found\n";
            for( uint i=0; i<lst.size(); ++i )
            {
                NODEREF<ServiceCoid> ns;
                e = SRVROOTA._objmgr.get_node( lst[i], ns );
                if(!e)
                {
                    charstr t;
                    bs << i << ". " << name << "-" << ns->_desc._version.get_version(t) << "\n";
                }
            }
            bs << EOM;
            return ersFE_INVALID_SVC_VER;
        }
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd AcceptorCoid::find_instance( uint soid, const token& name, const version& svcver, NODEREF<ServiceInstanceCoid>& psvc )
{
    opcd e = SRVROOTA._objmgr.serviceinst_find( soid, name, svcver, psvc );
    if(e)
    {
        charstr ver;
        binstream& bin = post_error(0);
        bin << "error getting service object, client \""
            << name << "-" << svcver.get_version(ver) << "\" error: ";

        if( e >= ersFE_NO_SUCH_SERVICE )
        {
            bin << "\n";

            dynarray<uint> lst;
            SRVROOTA._objmgr.service_find_all( name, lst );

            charstr ver;
            if( lst.size() == 0 )
            {
                bin << "no such service" << EOM;
                return ersFE_NO_SUCH_SERVICE;
            }
            else
            {
                bin << "no matching version of service \"" << name << "\" can be found:\n";
                for( uint i=0; i<lst.size(); ++i )
                {
                    NODEREF<ServiceCoid> ns;
                    e = SRVROOTA._objmgr.get_node( lst[i], ns );
                    if(!e)
                    {
                        charstr t;
                        bin << i << ". " << name << "-" << ns->_desc._version.get_version(t) << "\n";
                    }
                }
                bin << EOM;
                return ersFE_INVALID_SVC_VER;
            }
        }
        else
            bin << opcd_formatter(e) << EOM;
    }

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd AcceptorCoid::attach_svci( uint& connflags, ServiceCoid* svc, NODEREF<ServiceInstanceCoid>& svci )
{
    svc->_desc.check_access(connflags);

    if( (connflags & StdProtocolMethod::ConnectFlags::xACCESS_MODE) == 0 )
    {
        if( verbosity_level_ok(2) )
        {
            char bufc[4], bufs[4];
            StdProtocolMethod::ConnectFlags::get_access_mode_string( connflags, bufc );
            StdProtocolMethod::ConnectFlags::get_access_mode_string( svc->_desc._flags, bufs );
            post_error(2) << "client requires [" << bufc << "] access mode, server allows [" << bufs << "] mode" << EOM;
        }
        return ersFE_NO_ACCESS_MODE_FITS "service descriptor filtered";
    }

    local<ServiceInstanceCoid> inst = new ServiceInstanceCoid( &svc->_desc, svc->_path );

    void* optr=0;
    opcd e;
    e = svc->instantiate( &optr, inst );
    if(!e)
        e = SRVROOTA.attach( inst.ptr(), svci, svc->get_id(), 0 );

    if(!e)
        inst.eject();

    if(!e) {
        e = svci->dispatch( StdDispatchMethod::POSTCONSTRUCTOR, nullstream, 0 );
        DASSERT( e != ersNOT_IMPLEMENTED );     //old coidgen used, regenerate
    }

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd AcceptorCoid::reattach_svci( uint& connflags, NODEREF<ServiceInstanceCoid>& svci )
{
    svci->_desc->check_access(connflags);
    if( (connflags & StdProtocolMethod::ConnectFlags::xACCESS_MODE) == 0 )
    {
        if( verbosity_level_ok(2) )
        {
            char bufc[4], bufs[4];
            StdProtocolMethod::ConnectFlags::get_access_mode_string( connflags, bufc );
            StdProtocolMethod::ConnectFlags::get_access_mode_string( svci->_desc->_flags, bufs );
            post_error(2) << "client requires [" << bufc << "] access mode, server allows [" << bufs << "] mode" << EOM;
        }
        return ersFE_NO_ACCESS_MODE_FITS "service descriptor filtered";
    }

    // shared access
    if( (connflags & StdProtocolMethod::ConnectFlags::fUNIQUE_ACCESS) != 0 )
        return ersNOT_IMPLEMENTED;

    if( !(svci->_flags & ServiceInstanceCoid::fALLOW_SHARED) )
    {
        if( !(svci->_flags & ServiceInstanceCoid::fAUTOINSTANCE) )
            post_error(1) << "shared connection to object not (yet) permitted: "
            << svci->class_name() << ":" << svci->object_name() << " (id " << svci->get_id() << ")" << EOM;
        return ersFE_NOT_YET_READY;
    }

    //connect to shared object
    post_trace(0) << "attaching shared connection to service: " << svci->class_name() << ":" << svci->object_name()
        << " (id " << svci->get_id() << ")" << EOM;

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
void AcceptorCoid::on_exit_signalled()
{
	_acceptor.close();
}









////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
opcd CoidAcceptorCoid::check_server_version( header& hdr ) const
{
    if( hdr.coidver.get_major() != GserverVersion.get_major() )
    {
        charstr host;
        charstr ver1,ver2;

        post_error(0) << "client of service \"" << hdr.service << "\"; different COID version: client compiled with coid-"
        << hdr.coidver.get_version(ver1) << "; current server: coid-" << GserverVersion.get_version(ver2)
        << ".\nThe request came from: " << hdr.addr.getHost(host, true) << EOM;
        return ersFE_INVALID_SERVER_VER;
    }

    if( hdr.coidver.get_minor() != GserverVersion.get_minor() )
    {
        // if COID minor versions differ, the only supported mode is REMOTE
        if( (hdr.flags & StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE) == 0 )
        {
            charstr ver1,ver2;
            charstr host;

            post_error(0) << "client of service \"" << hdr.service << "\"; different COID version: client compiled with coid-"
            << hdr.coidver.get_version(ver1) << "; current server: coid-" << GserverVersion.get_version(ver2)
            << ".\nOnly the remote mode is allowed in this case.\nThe request came from: " << hdr.addr.getHostName(host, true) << EOM;
            return ersFE_INVALID_SERVER_VER;
        }
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
///@note never flush here
opcd CoidAcceptorCoid::attach_service_remote( binstream& bin, const netAddress& addr, uint* svciid )
{
    header hdr;

    NODEREF<ServiceInstanceCoid> svci;

    hdr.addr = addr;
    hdr.flags = StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE | StdProtocolMethod::ConnectFlags::fACCESS_MODE_INTERPROCESS;

    opcd e = attach_service_inst( bin, hdr, svci );
    if(!e) {
        *svciid = svci->get_id();
        svci.unset();
    }

    return e;
}


////////////////////////////////////////////////////////////////////////////////
///Attach service for remote client
opcd CoidAcceptorCoid::attach_service_remote( uint oid, token name_ver, binstream& bin, metastream* meta, uint* svciid )
{
    opcd e;
    try
    {
        NODEREF<ServiceInstanceCoid> svci;

        e = attach_service_inst( oid, name_ver, bin, meta, svci );
        if(!e) {
            *svciid = svci->get_id();
            svci.unset();
        }
    }
    catch( thread::Exception & )
    {
        post_alert(0) << "acceptor thread was cancelled" << EOM;
        throw;
    }
//#ifdef COID_CATCH_GLOBAL_EXCEPTIONS
    catch (opcd cd)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in acceptor. Error: "
            << opcd_formatter(cd)
            << " upon request for remote: " << name_ver << EOM;
        e = cd;
    }
    catch (...)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in acceptor. (a non-opcd type thrown)"
            << " upon request for remote: " << name_ver << EOM;
        e = ersEXCEPTION;
    }
//#endif

    if(e)
        post_trace(3) << "remote attach request failed" << EOM;

    return e;
}

////////////////////////////////////////////////////////////////////////////////
///Attach service for remote client
opcd CoidAcceptorCoid::attach_service_inst( uint oid, token name_ver, binstream& bin, metastream* meta, NODEREF<ServiceInstanceCoid>& svci )
{
    LNID attln = UMAX32;

    token svcname = name_ver.cut_left('.');
    version svcver;
    
    if( name_ver.is_empty() )
        svcver._build = UMAX32;   //match any
    else
        svcver.set(name_ver);

    opcd e;
    if( oid == UMAX32 )      // new object
    {
        NODEREF<ServiceCoid> svc;
        svci.set_atexit_detach();

        e = find_service( svcname, svcver, svc );
        if(e) return e;

        uint connflg = StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE;
        e = attach_svci( connflg, svc, svci );
        if(e)  return e;

        e = svci->dpx_authentify( bin, meta );
		if( e == ersNOT_IMPLEMENTED )
			e = 0;

        if(!e)
            svci->_flags |= ServiceInstanceCoid::fAUTHENTIFIED;
    }
    else    //shared attach
    {
        e = find_instance( oid, svcname, svcver, svci );
        if(e) return e;

        uint connflg = StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE;
        e = reattach_svci( connflg, svci );
        if(e) return e;

        e = svci->dpx_authentify_shared( bin, meta );
		if( e == ersNOT_IMPLEMENTED )
			e = 0;
    }

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidAcceptorCoid::attach_to_connection( NODEREF<ConnectionCoid>& con, binstream& bin, const netAddress& addr )
{
    NODEREF<ServiceInstanceCoid> svci(true);
    header hdr;

    hdr.addr = addr;
    if( con.is_set() )
        hdr.flags = StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE | StdProtocolMethod::ConnectFlags::fACCESS_MODE_INTERPROCESS;
    else
        hdr.flags = StdProtocolMethod::ConnectFlags::fACCESS_MODE_DIRECT;

    opcd e = attach_service_inst( bin, hdr, svci );
    if(!e)
    {
        if( hdr.mode == 0 )     //direct
        {
            hdr.cinfo._ifacethis = hdr.ithis;
            hdr.cinfo._disconnect_interface =
                (CreateInterfaceInfo::t_fnc) &ServiceInstanceCoid::disconnect_interface_catch;
            hdr.cinfo._mutex = &svci.get_mutex()->get_mutex();

            svci->dpx_create_interface( hdr.cinfo, bin );
		    //svci->deinit(false);

            //unset without executing detach (if any)
            svci.unset();
        }
        else
        {
            if( hdr.soid == UMAX32 )      //new object
                svci->_owner = con->_id;

            uint chid = con->add_service_inst( svci );
            bin << chid;

            svci.unset();
        }
    }

    bin << BINSTREAM_FLUSH;

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidAcceptorCoid::attach_new_connection( local<netstream>& bin, const netAddress& addr )
{
    NODEREF<ServiceInstanceCoid> svci;
    header hdr;

    hdr.addr = addr;
    hdr.flags = StdProtocolMethod::ConnectFlags::xACCESS_MODE;

    opcd e = attach_service_inst( *bin, hdr, svci );
    if(!e)
    {
        if( hdr.mode == 0 )     //direct
        {
            hdr.cinfo._ifacethis = hdr.ithis;
            hdr.cinfo._disconnect_interface =
                (CreateInterfaceInfo::t_fnc) &ServiceInstanceCoid::disconnect_interface_catch;
            hdr.cinfo._mutex = &svci.get_mutex()->get_mutex();

            svci->dpx_create_interface( hdr.cinfo, *bin );
		    //svci->deinit(false);

            *bin << BINSTREAM_FLUSH;

            //unset without executing detach (if any)
            svci.unset();
        }
        else
        {
            NODEREF<ConnectionCoid> conn;
            SRVROOTA.attach( new ConnectionCoid, conn, _id, 1 );
            
            binstream& binx = *bin.ptr();
            conn->init( bin.eject(), addr, hdr.nick );
            conn->set_active_conn();

            if( hdr.soid == UMAX32 )      //new object
                svci->_owner = conn->_id;

            uint chid = conn->add_service_inst( svci );
            binx << chid << BINSTREAM_FLUSH;

            svci.unset();

            //we use this thread to run the dispatch loop
            // con._detach flag will cause it to kill itself automatically upon exit
            conn->loop();
			ConnectionCoid::clear_active_conn();
            ThreadMgr::set_thread_name( thread::self(), _thrname );
            ThreadMgr::set_thread_tempid( thread::self(), _id );
        }
    }
    else
        *bin << BINSTREAM_FLUSH;

    return e;
}

////////////////////////////////////////////////////////////////////////////////
///Never flush
opcd CoidAcceptorCoid::attach_service_inst( binstream& bin, header& hdr, NODEREF<ServiceInstanceCoid>& svci )
{
    LNID attln = UMAX32;

    opcd e;
    try
    {
        uchar id;

        bin >> id;
        if( id != _conchar )
        {
            bin.acknowledge(true);
            e = ersFE_UNRECG_REQUEST;
            bin << e;
            return e;
        }

        uint flg = hdr.flags | ConnectFlags::fPING_SERVER;	// adjust mask

        bin >> hdr;
        hdr.flags &= flg;

        bool samemachine = hdr.addr.isLocalHost()  ||  hdr.addr.equal( _accp_addr );
        bool sameprocess = samemachine  &&  hdr.pid == _pid;

        hdr.set_access_flags( hdr.flags, samemachine, sameprocess );

        e = check_server_version(hdr);
        if(e)
        {
            bin >> BINSTREAM_ACK_EAT;
            bin << e;
            return e;
        }


        //NODEREF<ConnectionCoid,ATEXIT_DO_DETACH> mc;
        //uint mode;

        if( hdr.soid == UMAX32 )      // new object
        {
            NODEREF<ServiceCoid> svc;

            svci.set_atexit_detach();

            e = find_service( hdr.service, hdr.cinfo._clientver, svc );
            if(!e) {
				if( hdr.flags & ConnectFlags::fPING_SERVER ) {
					bin.acknowledge();
					bin << opcd(0);
					return ersEXIT;
				}
                e = attach_svci( hdr.flags, svc, svci );
            }

            if(e)
            {
                bin >> BINSTREAM_ACK_EAT;
                bin << e;
                return e;
            }
            else
            {
                e = svci->dpx_authentify(bin,0);

				if( e == ersNOT_IMPLEMENTED )
					e = 0;

                if(!e)
                    svci->_flags |= ServiceInstanceCoid::fAUTHENTIFIED;
            }
        }
        else    //shared attach
        {
            //svci.set_atexit_detach(); 

            e = find_instance( hdr.soid, hdr.service, hdr.cinfo._clientver, svci );
            if(!e) {
				if( hdr.flags & ConnectFlags::fPING_SERVER ) {
					bin.acknowledge();
					bin << opcd(0);
					return ersEXIT;
				}
                e = reattach_svci( hdr.flags, svci );
            }

            if(e)
            {
                bin >> BINSTREAM_ACK_EAT;
                bin << e;
                return e;
            }
            else
            {
                e = svci->dpx_authentify_shared(bin,0);

				if( e == ersNOT_IMPLEMENTED )
					e = 0;
            }
        }

        if(!e)
        {
            hdr.get_mode();
            bin << hdr.flags;
        }
    }
    catch (thread::Exception &)
    {
        post_alert(0) << "acceptor thread was cancelled" << EOM;
        throw;
    }
//#ifdef COID_CATCH_GLOBAL_EXCEPTIONS
    catch (opcd cd)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in acceptor. Error: "
            << opcd_formatter(cd)
            << " upon request from: " << hdr.addr.getHostName(host, true) << EOM;
        e = cd;
    }
    catch (...)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in acceptor. (a non-opcd type thrown)"
            << " upon request from: " << hdr.addr.getHostName(host, true) << EOM;
        e = ersEXCEPTION;
    }
//#endif

    if(e)
        post_trace(3) << "attach request failed" << EOM;

    //if (e && attln != UMAX32)
    //    SRVROOTA.set_node( attln, 0 );

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidAcceptorCoid::accept_connections()
{
    while( !is_exit_signalled()  &&  !thread::self().should_cancel() )
    {
        if( !_semaphore.acquire() )
            return ersUNAVAILABLE "bailing out";

        --_nwaiting;

        if( is_exit_signalled()  ||  thread::self().should_cancel() )
        {
            _semaphore.release();
			break;
		}

        if( _nwaiting < 1 )
            spawn();

        netAddress addr;
        uints h = UMAXS;

        if( _acceptor.isValid() )
            try { h = _acceptor.accept( &addr ); } catch(opcd) {}

        //release next acceptor thread
        _semaphore.release();

        if( h != UMAXS )
        {
            local<netstream> pipe;
            pipe = new netstreamcoid(h);

            opcd ea = attach_new_connection( pipe, addr );
            if(!ea)
                pipe.eject();
        }

        //we'll help server with the corpses
        //SRVROOTA.prune_dead();
        ++_nwaiting;
    }

    if( is_exit_signalled() )      //forced exit
    {
        post_term() << EOM;
        SRVROOTA.detach( _id, 0 );
    }

    return 0;
}








////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
opcd UniAcceptorCoid::init( int port, NODEREF<ServiceCoid>& service )
{
    //make dependency so the pointer remains valid
    SRVROOTA._objmgr.object_hook( get_id(), service->get_id() );
    _svc = service;

    charstr name = service->object_name();
    name += " acceptor";
    opcd e = AcceptorCoid::init( port, name );

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd UniAcceptorCoid::accept_connections()
{
    while( !is_exit_signalled() )
    {
        if( !_semaphore.acquire() )
            return ersUNAVAILABLE "bailing out";
        --_nwaiting;

        if( is_exit_signalled()  ||  thread::self().should_cancel() )
        {
            _semaphore.release();
			break;
		}

        if( _nwaiting < 1 )
            spawn();

        netAddress addr;
        uints h = UMAXS;
        if( _acceptor.isValid() )
            try { h = _acceptor.accept( &addr ); } catch(opcd) {}

        //release next acceptor thread
        _semaphore.release();

        if( h != UMAXS )
        {
            netSocket ns(h);
            opcd ea = attach_connection( ns, addr );
        }

        //we'll help server with the corpses
        ++_nwaiting;
    }

    if( is_exit_signalled() )      //forced exit
    {
        post_term() << "forced exit" << EOM;
        SRVROOTA.detach( _id, 0 );
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd UniAcceptorCoid::attach_connection( netSocket& bin, const netAddress& addr )
{
    LNID attln = UMAX32;

    opcd e;
    try
    {
        bool samemachine;
        uint flags = StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE
            | StdProtocolMethod::ConnectFlags::fUNIQUE_ACCESS;

        // check the access mode
        samemachine = addr.isLocalHost()  ||  _accp_addr == addr;

        NODEREF<ServiceInstanceCoid> svci;

        e = attach_svci( flags, _svc, svci );
        if(e)
            return e;

        ThreadMgr::set_thread_name( thread::self(), "process_connection" );
        ThreadMgr::set_thread_tempid( thread::self(), svci->_id );
        e = svci->dpx_dispatch_loop( &bin, _accp_addr.getPort() );
    }
    catch (thread::Exception &)
    {
        post_alert(0) << "acceptor thread was cancelled" << EOM;
        throw;
    }
//#ifdef COID_CATCH_GLOBAL_EXCEPTIONS
    catch (opcd cd)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in " << class_name() << ":" << object_name() << ". Error: "
            << opcd_formatter(cd)
            << " upon request from: " << addr.getHostName(host, true) << EOM;
        e = cd;
    }
    catch (...)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in " << class_name() << ":" << object_name() << ". (a non-opcd type thrown)"
            << " upon request from: " << addr.getHostName(host, true) << EOM;
        e = ersEXCEPTION;
    }
//#endif

    ThreadMgr::set_thread_name( thread::self(), _thrname );
    ThreadMgr::set_thread_tempid( thread::self(), _id );

    if(e)
        post_trace(3) << "attach request failed" << EOM;

    //if (e && attln != UMAX32)
    //    SRVROOTA.set_node( attln, 0 );

    return e;
}


////////////////////////////////////////////////////////////////////////////////
opcd UniBoundAcceptorCoid::accept_connections()
{
    //thread-local service instance
    uint flags = StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE
        | StdProtocolMethod::ConnectFlags::fUNIQUE_ACCESS;

    NODEREF<ServiceInstanceCoid> svci;

    opcd e = attach_svci( flags, _svc, svci );
    if(e)
        return e;


    while( !is_exit_signalled() )
    {
        if( !_semaphore.acquire() )
            return ersUNAVAILABLE "bailing out";
        --_nwaiting;

        if( _nwaiting < 1 )
            spawn();

        netAddress addr;
        uints h = UMAXS;
        if( _acceptor.isValid() )
            try { h = _acceptor.accept( &addr ); } catch(opcd) {}

        //release next acceptor thread
        _semaphore.release();

        if( h != UMAXS )
        {
            netSocket ns(h);
            opcd ea = process_connection( svci, ns, addr );
        }

        ++_nwaiting;
    }


    if( is_exit_signalled() )      //forced exit
    {
        svci.set_atexit_nothing();

        post_term() << "forced exit" << EOM;
        SRVROOTA.add_to_dead_list( get_id() );
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd UniBoundAcceptorCoid::process_connection( ServiceInstanceCoid* svci, netSocket& bin, const netAddress& addr )
{
    opcd e;
    try
    {
        ThreadMgr::set_thread_name( thread::self(), "process connection" );
        ThreadMgr::set_thread_tempid( thread::self(), svci->_id );
        e = svci->dpx_dispatch_loop( &bin, _accp_addr.getPort() );
    }
    catch (thread::Exception &)
    {
        post_alert(0) << "UniBoundAcceptorCoid thread was cancelled" << EOM;
        throw;
    }
//#ifdef COID_CATCH_GLOBAL_EXCEPTIONS
    catch (opcd cd)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in " << class_name() << ":" << object_name() << ". Error: "
            << opcd_formatter(cd)
            << " upon request from: " << addr.getHostName(host, true) << EOM;
        e = cd;
    }
    catch (...)
    {
        charstr host;
        post_critical(0) << "unhandled exception occured in " << class_name() << ":" << object_name() << ". (a non-opcd type thrown)"
            << " upon request from: " << addr.getHostName(host, true) << EOM;
        e = ersEXCEPTION;
    }
//#endif

    ThreadMgr::set_thread_name( thread::self(), _thrname );
    ThreadMgr::set_thread_tempid( thread::self(), _id );

    return e;
}


/*
////////////////////////////////////////////////////////////////////////////////
bool TowerCoid::spawn()
{
    if (0 == spawn_thread( thread_fnc, this, true, "listener" ))
        return true;
    return false;
}

////////////////////////////////////////////////////////////////////////////////
void* TowerCoid::thread_fnc (void* p)
{
    opcd e = ((TowerCoid*)p)->accept_connections( SRVROOTA.get_coidaccp_port() );
    return (void*) e._ptr;
}
*/

} // namespace coid
